package com.jack.netty.tcp.server;

import com.jack.netty.coder.tlv.TLVDecoder;
import com.jack.netty.coder.tlv.TLVEncoder;
import com.jack.netty.log.DefaultLogger;
import com.jack.netty.log.LoggerManager;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.timeout.IdleStateHandler;

import java.net.InetSocketAddress;

/**
 * Created by jack on 2015/5/21 0021.
 */
public class TLVServer {

    /**
     * 服务器监听端口
     */
    private int port = 8899;

    /**
     * 服务器名称，非必要字段，主要用户日志区别
     */
    private String serverName = "服务器";

    private String clientName = "客户端";

    /**
     * 通道读空闲时间
     */
    private int idleTimeRead = 5;

    /**
     * 通道写空闲时间
     */
    private int idleTimeWrite = 5;

    /**
     * 连接管理组
     */
    private EventLoopGroup bossGroup;

    /**
     * 工作组
     */
    private EventLoopGroup workGroup;

    /**
     * 服务器启动器
     */
    private ServerBootstrap serverBootstrap;

    /**
     * 服务器自动重启标示，true-会自动重启，false-不会自动重启
     */
    private boolean restartFlag = false;

    /**
     * 服务器自动重启间隔时间
     */
    private int restartTime = 3;

    /**
     * 消息监听器，必要参数
     */
    private TLVServerListener tlvServerListener;

    public TLVServer(){

    }

    /**
     * 启动服务器
     */
    public void start(){
        try{
            bossGroup = new NioEventLoopGroup();
            workGroup = new NioEventLoopGroup();
            serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup, workGroup);
            serverBootstrap.channel(NioServerSocketChannel.class);
            serverBootstrap.option(ChannelOption.SO_BACKLOG, 128);
            serverBootstrap.option(ChannelOption.TCP_NODELAY, true);
            serverBootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);
            serverBootstrap.handler(new LoggingHandler(LogLevel.INFO));
            serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel ch) throws Exception {
                    ch.pipeline().addLast(new IdleStateHandler(idleTimeRead, idleTimeWrite, 0));
                    ch.pipeline().addLast(new TLVDecoder());
                    ch.pipeline().addLast(new TLVEncoder());
                    ch.pipeline().addLast(new TLVServerHandler(tlvServerListener,clientName));
                }
            });
            ChannelFuture future = serverBootstrap.bind(port).sync();
            future.addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture f) throws Exception {
                    if(f.isSuccess()){
                        LoggerManager.getInstance().getiLogger().info(getClass(),serverName + "启动成功,端口:" + port);
                    }else{
                        if(restartFlag){
                            LoggerManager.getInstance().getiLogger().info(getClass(),serverName + "启动失败，重新启动中...");
                            restart();
                        }
                    }
                }
            });
            future.channel().closeFuture().sync();
            LoggerManager.getInstance().getiLogger().info(getClass(),serverName + "关闭");
        }catch (Exception e){
            LoggerManager.getInstance().getiLogger().error(getClass(),serverName + "启动异常:" + e.getMessage());
            if(restartFlag){
                restart();
            }
        }finally {
            if(restartFlag){
                restart();
            }else{
                bossGroup.shutdownGracefully();
                workGroup.shutdownGracefully();
                LoggerManager.getInstance().getiLogger().error(getClass(),serverName + "总关闭");
            }
        }
    }

    /**
     * 内部重新启动服务器
     */
    private void restart(){
        try {
            Thread.sleep(restartTime*1000);
        } catch (InterruptedException e1) {
            LoggerManager.getInstance().getiLogger().error(getClass(),e1.getMessage());
        }
        start();
    }

    /**
     * 手动关闭服务器
     */
    public void stop(){
        restartFlag = false;
        bossGroup.shutdownGracefully();
        workGroup.shutdownGracefully();
        LoggerManager.getInstance().getiLogger().error(getClass(),serverName + "总关闭");
    }


    /**
     * 向指定用户发送消息
     * @param message 消息体
     * @param uid 客户端id，不定长变量
     */
    public void sendMessage(String message,String... uid){
        LoggerManager.getInstance().getiLogger().info(getClass(),"准备发送消息到"+clientName+",uid:"+uid +",msg:" + message);
        if(uid != null){
            for(String id : uid){
                ChannelHandlerContext ctx = ServerSessionManager.get(id);
                if(ctx != null){
                    ctx.writeAndFlush(message);
                    LoggerManager.getInstance().getiLogger().info(getClass(),"发送消息到:"+id+" 成功");
                }
            }
        }
    }

    /**
     * 向所有在线客户端广播消息
     * @param message 消息体
     */
    public void sendMessageAll(String message){
        LoggerManager.getInstance().getiLogger().info(getClass(),"广播到所有"+clientName+":" + message);
        for(ChannelHandlerContext ctx : ServerSessionManager.getContexts()){
            if(ctx != null){
                ctx.writeAndFlush(message);
            }
        }
    }

    /**
     * 向除指定ID以为的客户端发送消息
     * @param message 消息体
     * @param uid 排除在外的客户端ID
     */
    public void sendMessageAllExceptMe(String message,String uid){
        LoggerManager.getInstance().getiLogger().info(getClass(),"发送到其他"+clientName +":" + message);
        if(uid != null){
            for(ChannelHandlerContext ctx : ServerSessionManager.getContextExceptOne(uid)){
                if(ctx != null){
                    ctx.writeAndFlush(message);
                }
            }
        }
    }

    /**
     * 外部调用拒绝客户端连接，用于业务合法性验证等调用
     * @param uid
     * @param refuseMessage
     */
    public void refuseClient(String uid,String refuseMessage){
        LoggerManager.getInstance().getiLogger().info(getClass(),"拒绝"+clientName+"登录,uid:"+uid+" refuse message:"+refuseMessage);
        if(uid != null){
            ChannelHandlerContext ctx = ServerSessionManager.get(uid);
            if(ctx != null){
                ctx.writeAndFlush(refuseMessage);
                ctx.close();
            }
        }
    }

    /**
     * 获取客户端IP
     * @param uid
     * @return
     */
    public String getClientIp(String uid){
        String ip = null;
        if(uid != null){
            ChannelHandlerContext ctx = ServerSessionManager.get(uid);
            if(ctx != null){
                ip = ((InetSocketAddress)ctx.channel().remoteAddress()).getAddress().getHostAddress();
            }
        }
        LoggerManager.getInstance().getiLogger().info(getClass(),"获取"+clientName+":"+uid+" IP:"+ip);
        return ip;
    }

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }

    public String getServerName() {
        return serverName;
    }

    public void setServerName(String serverName) {
        this.serverName = serverName;
    }

    public int getIdleTimeRead() {
        return idleTimeRead;
    }

    public void setIdleTimeRead(int idleTimeRead) {
        this.idleTimeRead = idleTimeRead;
    }

    public int getIdleTimeWrite() {
        return idleTimeWrite;
    }

    public void setIdleTimeWrite(int idleTimeWrite) {
        this.idleTimeWrite = idleTimeWrite;
    }

    public boolean isRestartFlag() {
        return restartFlag;
    }

    public void setRestartFlag(boolean restartFlag) {
        this.restartFlag = restartFlag;
    }

    public int getRestartTime() {
        return restartTime;
    }

    public void setRestartTime(int restartTime) {
        this.restartTime = restartTime;
    }

    public TLVServerListener getTlvServerListener() {
        return tlvServerListener;
    }

    public void setTlvServerListener(TLVServerListener tlvServerListener) {
        this.tlvServerListener = tlvServerListener;
    }

    public String getClientName() {
        return clientName;
    }

    public void setClientName(String clientName) {
        this.clientName = clientName;
    }

    public static void main(String[] args){
        LoggerManager.getInstance().setiLogger(new DefaultLogger());
        final TLVServer tlvServer = new TLVServer();
        tlvServer.setServerName("jack服务器");
        tlvServer.setClientName("手机端");
        tlvServer.setRestartFlag(true);
        tlvServer.setPort(53654);
        tlvServer.setTlvServerListener(new SampleServerListener());
        new Thread(new Runnable() {
            @Override
            public void run() {
                tlvServer.start();
            }
        }).start();


        //tlvServer.sendMessage("测试消息","jack","tom");
    }
}
